from querier.esquerier import ElasticSearchQuerier
import utils.utils as utils
import math
READ = 'read_num'
LIKE = 'like_num'
RATIO = 'like_read_ratio'
RELATIVE = 'relative'


class WechatArticleMatchQuerier(ElasticSearchQuerier):
    def __init__(self, es, index, doc_type, nlp_service=None):
        super(WechatArticleMatchQuerier, self).__init__(es, index, doc_type)
        self.nlp_service = nlp_service

    def _build_query(self, args):
        biz_code = args.get('biz_code', None)
        if biz_code is None:
            raise ValueError('"biz_code" is needed.')
        term = args.get('term', '')
        if not term:
            term = ''
        filters = args.get('filters', {})
        order = args.get('order_by', utils.ORDER_OVERALL)
        from_ = args.get('from', 0)
        size_ = args.get('size', 10)
        highlight = args.get('highlight', False)

        term2, keywords, ex_keywords, weights = utils.process_query_term(term, self.nlp_service)
        ex_kw = utils.get_kv_json(ex_keywords, weights)
        ex_category = self.nlp_service.classify(keywords)
        ex_category = ex_category.get('classify', {})
        ex_category = utils.get_kv_json(ex_category.get('category', []), ex_category.get('category_prob', []))

        query = self._genQuery(biz_code, ' '.join(keywords), filters, order, from_, size_, highlight)
        return query, {}, {'keywords': keywords, 'order': order, 'ex_keywords': ex_kw, "ex_category": ex_category}

    def _build_result(self, es_result, param):
        keywords = param['keywords']
        order = param['order']
        total = es_result['hits']['total']
        posts = []
        for hit in es_result['hits']['hits']:
            posts.append(self.extractResult(hit, order))
        return {
            'total': total,
            'keywords': keywords,
            'articles': posts
        }

    def _genQuery(self, biz_code, query_keywords, filters, order, from_, size_, highlight):
        must_clause = []
        filter_clause = []

        if filters:
            filter_clause = self._addFilterRangeClause(filter_clause, filters, 'publish_timestamp')
            filter_clause = self._addFilterRangeClause(filter_clause, filters, 'read_num')
            filter_clause = self._addFilterRangeClause(filter_clause, filters, 'like_num')
            filter_clause = self._addFilterRangeClause(filter_clause, filters, 'like_read_ratio')
            filter_clause = self._addFilterClause(filter_clause, filters, 'category', 'should')
            filter_clause = self._addFilterClause(filter_clause, filters, 'brands', 'should')

        if query_keywords:
            must_clause.append(
                    {
                        'query_string': {
                            'query': query_keywords,
                            'fields': ['title',  'keywords'],
                            'minimum_should_match': utils.MINIMUM_SHOULD_MATCH
                        }
                    }
            )

        query = {'query': {
            'bool': {
                'must': must_clause,
                'filter': [{'term': {'biz_code': biz_code}}] + filter_clause
            }
        }, 'from': from_, 'size': size_}

        if order == READ:
            query['sort'] = [
                {'read_num': 'desc'},
                '_score'
            ]
        elif order == LIKE:
            query['sort'] = [
                {'like_num': 'desc'},
                '_score'
            ]
        elif order == RATIO:
            query['sort'] = [
                {'like_read_ratio': 'desc'},
                '_score'
            ]
        else:
            query['sort'] = [
                {'publish_timestamp': 'desc'},
                '_score'
            ]


        if highlight:
            query['highlight'] = {
                "pre_tags": ["<span class='keyword'>"],
                "post_tags": ["</span>"],
                "fields": {"keywords": {}, "title": {}}
            }

        return query

    def _addFilterMatch(self, must_clause, filters, key):
        if key in filters:
            if filters[key]:
                clause = []
                must_clause.append({
                    'bool': {'should': clause}
                })
                values = filters[key]
                if isinstance(values, str):
                    values = values.split(' ')
                for fk in values:
                    clause.append({'match': {key: {'query': fk, 'minimum_should_match': '10<85% 20<10'}}})
        return must_clause

    def _addFilterClause(self, filter_clause, filters, key, cond='must'):
        if key in filters:
            if filters[key]:
                clause = []
                filter_clause.append({
                    'bool': {
                        cond: clause
                    }
                })
                for fk in filters[key]:
                    clause.append({'term': {key: fk}})
        return filter_clause

    def _addFilterRangeClause(self, filter_clause, filters, key):
        if key in filters:
            if filters[key]:
                clause = []
                filter_clause.append({
                    'bool': {
                        'must': clause
                    }
                })
                fk = filters[key]
                if not isinstance(fk, list) or len(fk) < 1:
                    pass
                else:
                    min_fk = fk[0]
                    if len(fk) >= 2:
                        max_fk = fk[1]
                    else:
                        max_fk = None
                    if min_fk is not None and min_fk != 'null':
                        clause.append({'range': {key: {"gte": min_fk}}})
                    if max_fk is not None and max_fk != 'null':
                        clause.append({'range': {key: {"lte": max_fk}}})
        return filter_clause


    def extractResult(self, hit, order):
        source_ = hit['_source']
        url = source_['url']
        likes = source_['like_num']
        reads = source_['read_num']

        score_ = hit['_score']
        keywords = source_['keywords']
        # post_influence = math.log10(reads + 1) * 158
        # if order == utils.ORDER_RELATIVE:
        #     post_relative = score_
        # elif order == utils.ORDER_INFLUENCE:
        #     post_relative = score_
        # else:
        #     post_relative = score_ / math.log10(post_influence + 2)
        #
        # # normalize post_relative
        # post_relative = math.log2(1 + post_relative)
        # post_relative = min(1, post_relative)
        title = source_['title']
        highlight = hit.get('highlight')
        # keywords = highlight.get('keywords')
        if highlight:
            title = highlight.get('title') if highlight.get('title') else title
            keywords = highlight.get('keywords') if highlight.get('keywords') else keywords

        return {
            'biz_code': source_['biz_code'],
            'biz_name': source_['biz_name'],
            'title': title,
            'url': url,
            'msg_cdn_url': source_['msg_cdn_url'],
            'keywords': keywords,
            'read_num': reads,
            'like_num': likes,
            'publish_timestamp': source_['publish_timestamp'],
            'crawler_timestamp': source_['crawler_timestamp'],
            'category': source_.get('category'),
            'brands': self.nlp_service.get_brands(source_.get('keywords', []))
        }
